﻿using LightCAD.Core;
using Microsoft.CodeAnalysis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LightCAD.Runtime
{
    public enum ActionType
    {
        InsertObject,
        RemoveObject,
        SetProperty,
        SetUserData
    }
    public class ActionItem
    {
        public ActionType Type { get; set; }
        public DateTime CreateTime { get; set; }
        public ElementSpace ElementSet { get; set; }
        public LcObject Object { get; set; }
        public string ProertyName { get; set; }
        public LcPropertyGroup ExtPropertyGroup { get; set; }
        public LcProperty ExtProperty { get; set; }
        public object OldValue { get; set; }
        public object NewValue { get; set; }
    }
    public class ActionRecord
    {
        public string Name { get; set; }
        public DateTime StartTime { get; set; }
        public DateTime EndTime { get; set; }
        public List<ActionItem> Actions { get; set; }
        public ActionRecord(string name)
        {
            this.Name = name;
            this.Actions = new List<ActionItem>();
            this.StartTime = DateTime.Now;
        }
    }
    public class DocumentRecorder
    {
        public const string InsertActionName = "InsertObject";
        public const string RemoveActionName = "RemoveObject";
        public const string SetPropertyName = "SetProperty";
        public const string DefaultActionName = "DocChange";


        public LcDocument Document { get; private set; }

        public List<ActionRecord> Records = new List<ActionRecord>();

        public int CurrentRecordIndex { get; internal set; }

        public string CurrentActionName { get; internal set; }

        public bool IsStopRecord { get; set; }
        public ActionRecord CurrentActionRecord { get; internal set; }
        public DocumentRecorder(LcDocument docment)
        {
            this.Document = docment;
        }
        public void AttachChangeEvents()
        {
            this.Document.ObjectChangedBefore += Document_ObjectChangedBefore;
            this.Document.ObjectChangedAfter += Document_ObjectChangedAfter;
            //this.Document.PropertyChangedBefore += Document_PropertyChangedBefore;
            this.Document.PropertyChangedAfter += Document_PropertyChangedAfter;
        }

        public void DetachChangeEvents()
        {
            this.Document.ObjectChangedBefore -= Document_ObjectChangedBefore;
            this.Document.ObjectChangedAfter -= Document_ObjectChangedAfter;
            // this.Document.PropertyChangedBefore -= Document_PropertyChangedBefore;
            this.Document.PropertyChangedAfter -= Document_PropertyChangedAfter;
        }

        public void BeginAction(string actionName = null)
        {
            if (IsStopRecord) return;

            if (this.CurrentActionName != null)
            {
                throw new InvalidOperationException("PreActionNotEnd");
            }
            if (actionName == null) actionName = DefaultActionName;
            this.CurrentActionName = actionName;
            this.CurrentActionRecord = new ActionRecord(actionName);
        }
        public void EndAction()
        {
            if (IsStopRecord) return;
            if (this.CurrentActionName == null)
            {
                throw new InvalidOperationException("CurrentActionIsNull");
            }
            ClearRecordsFromIndex();//将索引点后面的记录清除
            this.CurrentActionRecord.EndTime = DateTime.Now;
            this.Records.Add(this.CurrentActionRecord);
            this.CurrentRecordIndex++;
            this.CurrentActionRecord = null;
            this.CurrentActionName = null;
        }
        public void CancleAction()
        {
            if (IsStopRecord) return;
            if (this.CurrentActionName == null)
            {
                throw new InvalidOperationException("CurrentActionIsNull");
            }
            UndoActionRecord(this.CurrentActionRecord);
            this.CurrentActionRecord = null;
            this.CurrentActionName = null;
        }


        public void UndoAction()
        {
            if (this.Records.Count == 0 || CurrentRecordIndex == 0) return;

            var indexRecord = this.Records[CurrentRecordIndex - 1];
            UndoActionRecord(indexRecord);
            CurrentRecordIndex--;
        }
        public void RedoAction()
        {
            if (this.Records.Count == 0 || CurrentRecordIndex == this.Records.Count) return;

            var indexRecord = this.Records[CurrentRecordIndex];
            RedoActionRecord(indexRecord);
            CurrentRecordIndex++;
        }
        private void UndoActionRecord(ActionRecord record)
        {
            for (var i = record.Actions.Count - 1; i >= 0; i--)
            {
                var action = record.Actions[i];
                var obj = action.Object;

                if (action.Type == ActionType.InsertObject || action.Type == ActionType.RemoveObject)
                {
                    var ele = obj as LcElement;
                    var eleSet = action.ElementSet;
                    var state = eleSet.IsFireChangedEvent;
                    eleSet.IsFireChangedEvent = false;

                    if (action.Type == ActionType.InsertObject)
                        eleSet.RemoveElement(ele);
                    else
                        eleSet.InsertElement(ele);

                    eleSet.IsFireChangedEvent = state;
                }
                else if (action.Type == ActionType.SetProperty)
                {
                    var state = obj.IsFireChangedEvent;
                    obj.IsFireChangedEvent = false;
                    obj.SetProperty(action.ProertyName, action.OldValue, false);
                    obj.IsFireChangedEvent = state;
                }
            }
        }
        private void RedoActionRecord(ActionRecord record)
        {
            for (var i = 0; i <= record.Actions.Count - 1; i++)
            {
                var action = record.Actions[i];
                var obj = action.Object;
                if (action.Type == ActionType.InsertObject || action.Type == ActionType.RemoveObject)
                {
                    var ele = obj as LcElement;
                    var eleSet = action.ElementSet;
                    var state = eleSet.IsFireChangedEvent;
                    eleSet.IsFireChangedEvent = false;

                    if (action.Type == ActionType.InsertObject)
                    {
                        eleSet.InsertElement(ele);
                    }
                    else 
                    {
                        eleSet.RemoveElement(ele);
                    }
                    eleSet.IsFireChangedEvent = state;

                }
                else if (action.Type == ActionType.SetProperty)
                {
                    var state = obj.IsFireChangedEvent;
                    obj.IsFireChangedEvent = false;
                    obj.SetProperty(action.ProertyName, action.NewValue, false);
                    obj.IsFireChangedEvent = state;
                }
            }
        }

        /// <summary>
        /// 清除所有操作记录
        /// </summary>
        public void ClearAllRecords()
        {
            CurrentRecordIndex = 0;
            this.Records.Clear();
        }
        /// <summary>
        /// 清除索引后的操作记录
        /// </summary>
        private void ClearRecordsFromIndex()
        {
            //索引在记录表最后，无需清除
            if (CurrentRecordIndex == this.Records.Count) return;
            var idx = this.Records.Count - 1;
            while (idx >= CurrentRecordIndex)
            {
                this.Records.RemoveAt(idx);
                idx--;
            }
        }
        private Dictionary<ObjectChangedEventArgs,ActionItem> _currentActionItems = new Dictionary<ObjectChangedEventArgs, ActionItem>();
        private void Document_ObjectChangedBefore(object? sender, ObjectChangedEventArgs e)
        {
            if (IsStopRecord || CurrentActionRecord == null) return;

            if (_currentActionItems.ContainsKey(e))
            {
                throw new Exception("event repeat.");
            }

            var currentActionItem = new ActionItem
            {
                ElementSet = e.ElementSet,
                CreateTime = DateTime.Now
            };

            if (e.Type == ObjectChangeType.Insert)
                currentActionItem.Type = ActionType.InsertObject;
            else if (e.Type == ObjectChangeType.Remove)
            {
                currentActionItem.Type = ActionType.RemoveObject;
                currentActionItem.Object = e.Target;
            }

            _currentActionItems.Add(e, currentActionItem);
        }
        private void Document_ObjectChangedAfter(object? sender, ObjectChangedEventArgs e)
        {
            if (IsStopRecord || CurrentActionRecord == null) return;

            if (!_currentActionItems.TryGetValue(e,out ActionItem currentActionItem))
            {
                throw new Exception("event not found.");
            }
            if (e.Type == ObjectChangeType.Insert)
            {
                currentActionItem.Object = e.Target;
            }

            CurrentActionRecord.Actions.Add(currentActionItem);
            _currentActionItems.Remove(e);
        }
        //private void Document_PropertyChangedBefore(object? sender, PropertyChangedEventArgs e)
        //{
        //    if (IsStopRecord) return;
        //}

        private void Document_PropertyChangedAfter(object? sender, PropertyChangedEventArgs e)
        {
            if (IsStopRecord || CurrentActionRecord == null) return;

            var currentActionItem = new ActionItem
            {
                Type = ActionType.SetProperty,
                CreateTime = DateTime.Now,
                Object = e.Object,
                ProertyName = e.PropertyName,
                ExtProperty = e.ExtProperty,
                ExtPropertyGroup = e.ExtPropertyGroup,
                OldValue = e.OldValue,
                NewValue = e.NewValue,
            };
            CurrentActionRecord.Actions.Add(currentActionItem);
        }



    }
}
